package com.duowan.cms.parser;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import net.sf.json.JSONObject;
import net.sf.json.JSONSerializer;

import org.apache.commons.beanutils.DynaBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.duowan.cms.common.exception.BaseCheckedException;
import com.duowan.cms.common.util.DateUtil;
import com.duowan.cms.common.util.FileUtil;
import com.duowan.cms.common.util.PathUtil;
import com.duowan.cms.common.util.StringUtil;
import com.duowan.cms.common.webapp.content.SpringApplicationContextHolder;
import com.duowan.cms.common.webapp.property.ExtendedPropertyPlaceholderConfigurer.PropertiesHolder;
import com.duowan.cms.core.rmi.client.article.Article4TagListRemoteService;
import com.duowan.cms.core.rmi.client.channel.ChannelRemoteService;
import com.duowan.cms.core.rmi.client.hotword.HotWordRemoteService;
import com.duowan.cms.core.rmi.client.tag.TagRemoteService;
import com.duowan.cms.core.rmi.client.template.TemplateRemoteService;
import com.duowan.cms.dto.article.Article4TagListInfo;
import com.duowan.cms.dto.article.ArticleInfo;
import com.duowan.cms.dto.channel.ChannelInfo;
import com.duowan.cms.dto.hotword.HotWordInfo;
import com.duowan.cms.dto.tag.TagCategory;
import com.duowan.cms.dto.tag.TagInfo;
import com.duowan.cms.dto.template.TemplateCategory;
import com.duowan.cms.dto.template.TemplateInfo;
import com.duowan.cms.dto.template.TemplateStatus;
import com.duowan.cms.intf.gamelib.GameLibRemoteService;
import com.duowan.cms.parser.rmi.client.template.TemplateParserRemoteService;
import com.duowan.cms.parser.util.HtmlPage;
import com.duowan.cms.parser.util.PagerGenerator;
import com.duowan.cms.parser.util.Search;
import com.duowan.cms.parser.util.TemplateDataService;
import com.duowan.cms.parser.util.Tools;
import com.duowan.cms.parser.util.UserDefinedSyntaxParser;
import com.duowan.cms.parser.util.VMFactory;
import com.duowan.cms.parser.util.hotword.HotWordReplaceUtil;
import com.duowan.cms.parser.util.hotword.LexTree;

@Service("templateParserRemoteService")
public class TemplateParserServiceImpl implements TemplateParserService, TemplateParserRemoteService {

    @Autowired
    private TemplateRemoteService templateRemoteService;

    @Autowired
    private ChannelRemoteService channelRemoteService;

    // @Autowired
    // private ArticleRemoteService articleRemoteService;

    @Autowired
    private Article4TagListRemoteService article4TagListRemoteService;

    @Autowired
    private TagRemoteService tagRemoteService;

    @Autowired
    private HotWordRemoteService hotWordRemoteService;

    @Autowired
    private GameLibRemoteService gameLibRemoteService;
    
    private final static String PUBLIC_CHANNEL = "public";
    /**
     * 所有"生成文件"的生成记录，可用于辅助同步
     */
    private final static String GENERATE_FILE_LOG_FILE_PATH = PropertiesHolder.get("generateFileLogPath");

    /**
     * Rsync文件同步脚本本件，从本地机器同步分发到各个机器
     */
    private final static String RSYNC_SCRIPT_FILE_PATH = PropertiesHolder.get("rsyncScriptFile");

    @Override
    public void cleanUpArticleFile(String channelId, Long articleId) throws BaseCheckedException {
        // 因为有分页的情况，要进行批量删除
        ChannelInfo channelInfo = channelRemoteService.getById(channelId);
        int pageNo = 1;
        while (true) {
            String path = PathUtil.getAbsoluteArticlePath(channelRemoteService.getById(channelId).getArticleFilePath(), articleId, pageNo++);// 生成文件的目录
            if (!FileUtil.isFileExists(path)) {
                break;
            }
            this.writeFileWithLog(channelInfo, path, "");
        }
    }

    @Override
    public List<HtmlPage> parseTemplate(ChannelInfo channelInfo, TemplateInfo templateInfo) throws BaseCheckedException {
        // 1.获取模板，并做可用性判断
        if (channelInfo == null || templateInfo == null) {
            logger.error("解析模板是，模板不存在");
            throw new BaseCheckedException(ChannelRemoteService.CHANNEL_IS_NOT_EXITS, "频道或者模板不存在。");
        }

        String channelId = channelInfo.getId();
        Long templateId = templateInfo.getId();
        List<HtmlPage> htmlPages = null;
        logger.info("开始解析模板：channelid=" + channelId + ", templateId=" + templateId);
        // ChannelInfo channelInfo = channelRemoteService.getById(channelId);
        // 2.处理模板的内容
        this.getTemplateDataService(channelInfo);
        htmlPages = this.handleTemplateContent(channelInfo, templateInfo);
        return htmlPages;
    }

    /**
     * {[动态公共模板=公共模板中文名]} 公共模板中文名，必须是公共频道的模板 检查是否包含动态公共模板标签，如果有，先进行替换，再做解析
     * 
     * @param htmlBeforeVelocity Velocity解析前的内容
     * @return 把“{[动态公共模板=公共模板中文名]}”替换为公共模板内容后的模板内容
     */
    private String parseDynPublicTempl(String htmlBeforeVelocity) {

        Pattern pattern = Pattern.compile(".*", Pattern.CASE_INSENSITIVE | Pattern.DOTALL);
        Matcher matcher = pattern.matcher(htmlBeforeVelocity);

        String regexPublicTempl = "\\{\\[动态公共模板=([^\\]]+)\\]\\}";
        StringBuffer sbf = new StringBuffer();
        String parserStr = null;// 自定义标签解析结果
        while (matcher.find()) {
            parserStr = matcher.group(0);
            Pattern patternPublicTempl = Pattern.compile(regexPublicTempl, Pattern.CASE_INSENSITIVE | Pattern.DOTALL);
            Matcher matcherPublicTempl = patternPublicTempl.matcher(htmlBeforeVelocity);

            while (matcherPublicTempl.find()) {
                // 整个语法
                String syntax = matcherPublicTempl.group(0);
                // 等号右边 公共频道模板名
                String name = matcherPublicTempl.group(1);
                if (!StringUtil.isEmpty(name)) {
                    // 获得公共模板
                    TemplateInfo templateInfo = templateRemoteService.getByChannelIdAndTemplateName("public", name);
                    if (null != templateInfo) {
                        // 替换自定义语法
                        parserStr = parserStr.replace(syntax, templateInfo.getContent());
                    }
                }
            }
            sbf.append(parserStr + "\n");
        }
        pattern = Pattern.compile("(\\n){2,}", Pattern.CASE_INSENSITIVE | Pattern.DOTALL);
        String result = pattern.matcher(sbf.toString()).replaceAll("\n");
        return result;
    }

    /**
     * 处理模板的内容
     * 
     * 
     * @param channel
     * @param template
     * @return
     * @throws BaseCheckedException 
     */
    private List<HtmlPage> handleTemplateContent(ChannelInfo channelInfo, TemplateInfo templateInfo) throws BaseCheckedException {
        List<HtmlPage> htmlPages = new ArrayList<HtmlPage>();
        // 如果是“资讯推广”类型的模板，处理模板内容，并返回
        if (TemplateCategory.INFO_PROMOTION == templateInfo.getTemplateCategory()) {
            htmlPages = this.parseInfoPromotionTemplate(channelInfo, templateInfo);
        }
        // 如果是"栏目","专题","公共"类型的模板，处理模板内容，并返回
        else if (TemplateCategory.COLUMN == templateInfo.getTemplateCategory() || TemplateCategory.PUBLIC == templateInfo.getTemplateCategory()
                || TemplateCategory.TOPIC == templateInfo.getTemplateCategory()) {
            htmlPages = this.parseColumnTopicPublicTemplate(channelInfo, templateInfo);
        }
        // 其他的模板类别不需要解析
        else {
            logger.warn("模板：channelid=" + channelInfo.getId() + ", templateId=" + templateInfo.getId() + "类型是" + templateInfo.getTemplateCategory() + "，不需要解析。");
        }
        return htmlPages;
    }

    /**
     * 解析类型为“资讯推广”的模板
     * 
     * @param channel
     * @param template
     * @return
     * @throws BaseCheckedException 
     */
    private List<HtmlPage> parseInfoPromotionTemplate(ChannelInfo channelInfo, TemplateInfo templateInfo) throws BaseCheckedException {
        String result = null;
        final int pageNo = 1; // 只返回第一页，因为此类模板没有分页
        String channelId = channelInfo.getId();
        Long templateId = templateInfo.getId();
        logger.info("模板：channelid=" + channelId + ", templateId=" + templateId + "类型为‘资讯推广’,开始解析");
        String content = templateInfo.getContent(); // 当前模板的原内容（解析前）
        result = content;
        // 获取本频道下面的“配置文件“模板，如果有，加载配置文件到模板中；
        TemplateInfo configTemplate = this.getConfigTemplate(channelId);
        if (configTemplate != null) {
            logger.info("频道channelid=" + channelId + "有配置文件templateid=" + configTemplate.getId() + "，加载配置文件到此模板中.");
            String configContent = configTemplate.getContent();
            result = configContent + result;
        }
        // 获取公会新闻频道下面的“资讯推广公用”模板，如果有，加载公用模板进来；
        TemplateInfo ghInfoPromotionTemplate = this.getGHInfoPromotionTemplate();
        if (ghInfoPromotionTemplate != null) {
            logger.info("有资讯推广公用文件，加载配置文件到此模板中.");
            String globalContent = ghInfoPromotionTemplate.getContent();
            globalContent = globalContent.replace("[" + INFO_PROMOTION_TEMPLATE_NAME + "]", result);
            result = globalContent;
        }
        // 模板所需数据
        logger.info("增加模板：channelid=" + channelId + ", templateId=" + templateId + "所需要的数据。");
        Map<String, Object> data = new HashMap<String, Object>();
        data.put("channelid", channelId);
        VMFactory vmFactory = new VMFactory(result, data);
        // 解析模板内容中的自定义语法
        data.put("parseringObjectId", "模板id=" + templateInfo.getId());
        result = UserDefinedSyntaxParser.getInstance().parseTemplateQuote(channelInfo, templateInfo, vmFactory.getResultString());
        // 构造返回结果
        HtmlPage htmlPage = new HtmlPage(result, pageNo, templateInfo.getId(), templateInfo.getAlias(), templateInfo.getCode());
        List<HtmlPage> htmlPages = new ArrayList<HtmlPage>();
        htmlPages.add(htmlPage);
        return htmlPages;
    }

    // 解析类型为"栏目","专题","公共"的模板
    private List<HtmlPage> parseColumnTopicPublicTemplate(ChannelInfo channelInfo, TemplateInfo templateInfo) throws BaseCheckedException {
        List<HtmlPage> list = new ArrayList<HtmlPage>();
        String channelId = channelInfo.getId();
        long templateId = templateInfo.getId();
        String content = templateInfo.getContent();
        // 1. 获取本频道下面的“配置文件“模板，如果有，加载配置文件到模板内容中；
        TemplateInfo configTemplate = getConfigTemplate(channelId);
        if (null != configTemplate && !StringUtil.isEmpty(configTemplate.getContent())) {
            String temp = configTemplate.getContent().trim();
            if (logger.isDebugEnabled())
                logger.debug("频道：" + channelId + "有配置文件，加载配置文件到模板:" + configTemplate.getId() + "中.");
            content = this.removeAnnotation(configTemplate.getContent() ) + content;
        }
        // 2.解析动态调用公共模板
        content = parseDynPublicTempl(content);
        templateInfo.setContent(content);
        // 3.设置模板的数据容器
        Map<String, Object> data = new HashMap<String, Object>();
        data.put("guest", ""); // 置空
        data.put("tools", new Tools());
        data.put("data", this.getTemplateDataService(channelInfo));
        data.put("channelid", channelId);
        data.put("cooperate", templateInfo.getCooperate());
        data.put("alias", templateInfo.getAlias());
        data.put("search", new Search());
        data.put("channelname", channelInfo.getName());
        data.put("comment", UserDefinedSyntaxParser.getInstance().getTemplateCommentInclude(templateInfo.getUrlOnLine()));
        // TODO 获取专区在ku对应的游戏名，如果配置文件中找不到，则默认使用专区中文名
        String kuGameName = channelInfo.getName();
        /*
         * ChannelMapBean bean =
         * ChannelMapConfigHelper.getInstance().getValue(channelid); if(bean !=
         * null){ kuGameName = bean.getKuGameName(); }
         */
        data.put("kuGameName", kuGameName);
        List<String> results = dealWithTemplatePaging(templateInfo, data);
        int i = 0;
        for (String result : results) {
            result = UserDefinedSyntaxParser.getInstance().parseTemplateQuote(channelInfo, templateInfo, result);
            result = result.trim();
            list.add(new HtmlPage(result, ++i, templateId, templateInfo.getAlias(), templateInfo.getCode()));
        }
        return list;
    }

    private List<String> dealWithTemplatePaging(TemplateInfo templateInfo, Map<String, Object> data) throws BaseCheckedException {
        // 用于返回的list
        List<String> list = new ArrayList<String>();
        // 模板内容
        String content = templateInfo.getContent();
        // 自定义语法解析器
        UserDefinedSyntaxParser udsp = UserDefinedSyntaxParser.getInstance();
        // 分页解析，返回：1.content(被解析替换后的内容) 2.args(分页参数：总页数pages,每页显示数pagesize)
        List<String> contentAndArgs = udsp.parsePaging(content);
        boolean isNeedPage = (null != contentAndArgs && !contentAndArgs.isEmpty());
        if (isNeedPage) {
            content = contentAndArgs.get(0);
        }
        VMFactory vmFactory = new VMFactory(content, data);
        data.put("parseringObjectId", "模板id=" + templateInfo.getId());
        if (isNeedPage) {
            // 模板中的总页数
            int pages = Integer.parseInt(contentAndArgs.get(1));
            // 每页显示数
            int pagesize = contentAndArgs.size() > 2 ? Integer.parseInt(contentAndArgs.get(2)) : udsp.getPageSizeFromContent(content);
            // 实际的总页数
            int totalPageCount = 0;

            if (pagesize > 0) {
                // new List_db(channelid).getCount(tag);// 获得总文章数
                String tag = templateInfo.getRelatedtag().split(",")[0];
                String channelId = templateInfo.getChannelId();
                int articleCount = article4TagListRemoteService.getArticleCountByChannelIdAndTag(channelId, tag);
                articleCount = articleCount == 0 ? 1 : articleCount;// 没有文章的标签也要生成一页
                totalPageCount = (articleCount + pagesize - 1) / pagesize; // 计算得到总页数
            }

            totalPageCount = (totalPageCount > pages || totalPageCount == 0) ? pages : totalPageCount;
            String baseUrl = templateInfo.getAlias();
            if(!StringUtil.isEmpty(baseUrl)){
                baseUrl = baseUrl.substring(baseUrl.lastIndexOf("/") + 1);
            }else{
                baseUrl = "m_" + templateInfo.getId();
            }
            PagerGenerator pg = new PagerGenerator(baseUrl, totalPageCount);
            for (int i = 0; i < totalPageCount; i++) {
                StringBuffer sb = new StringBuffer();
                sb.append(pg.getPageHtml(i + 1));

                data.put("len", pagesize);
                data.put("pagecut", i);
                data.put("split_line", sb.toString());
                list.add(vmFactory.getResultString());
            }
        } else {
            data.put("split_line", "");
            list.add(vmFactory.getResultString());
        }
        return list;
    }
    private String removeAnnotation(String templateContent) {
    	return templateContent.replaceAll("((#\\*)((\\S|\\s)*?)(\\*#))|(\r\n)", " ");
	}
    @Override
    public List<HtmlPage> parseArticle(ChannelInfo channelInfo, ArticleInfo articleInfo) throws BaseCheckedException {

        String channelId = channelInfo.getId();
        Long articleId = articleInfo.getId();

        TemplateInfo templateInfo = templateRemoteService.getByChannelIdAndTemplateId(channelId, articleInfo.getTemplateId());
        if (templateInfo == null) {
            logger.error("专区" + channelId + "下文章" + articleId + "对应的模板" + articleInfo.getTemplateId() + "不存在。");
            throw new BaseCheckedException(TemplateRemoteService.TEMPLATE_IS_NOT_EXIST_IN_CHANNEL, "专区" + channelId + "下文章" + articleId + "对应的模板" + articleInfo.getTemplateId()
                    + "不存在。");
        }

        Map<String, Object> data = this.getTemplateDataFromArticleInfo(channelInfo, articleInfo);
        // TODO 检查是否有相关联的游戏
        // TODO 1，从游戏库取得游戏名片信, 2，发送资讯到游戏库（异步）
        String realGame = articleInfo.getRelateGame();
        if (!StringUtil.isEmpty(realGame)) {
            try {
                String jsonStr = gameLibRemoteService.getGameCardInfo(realGame);
                JSONObject jsonObject = (JSONObject) JSONSerializer.toJSON(jsonStr);
                DynaBean gameCardBean = (DynaBean) JSONSerializer.toJava(jsonObject);
                if (null != gameCardBean) {
                    data.put("gameCard", gameCardBean);
                    // 已经在游戏入发布器数据库时推送到游戏库系统
                    // gameLibRemoteService.sendArticle(realGame,
                    // articleInfo.getTitle(), articleInfo.getUrlOnLine());
                }
            } catch (Exception e) {
                logger.warn("游戏库接口获取游戏信息失败!", e);
            }
        }

        this.replaceHotWord(channelInfo, articleInfo);
        this.dealWithImagesInContent(articleInfo);

        List<String> results = dealWithArticlePaging(templateInfo, articleInfo, data);
        // 返回的list
        List<HtmlPage> htmlPages = new ArrayList<HtmlPage>();
        for (int pageNo = 0; pageNo < results.size(); pageNo++) {
            String result = results.get(pageNo);
            if (!StringUtil.isEmpty(result)) {
                result = parseDynPublicTempl(result);// 解析动态调用公共模板
                result = UserDefinedSyntaxParser.getInstance().parserSpecialChannel(result, channelId);// 特殊频道的处理
                result = UserDefinedSyntaxParser.getInstance().parseTemplateQuote(channelInfo, templateInfo, result); // 自定义语法解析
                htmlPages.add(new HtmlPage(result, pageNo, templateInfo.getId(), templateInfo.getAlias(), templateInfo.getCode()));
            }
        }
        return htmlPages;
    }

    /**
     *  返回的List的下标对应分页的页码，特别的，下标为0的内容是指“单页显示全部”的内容
     */
    private List<String> dealWithArticlePaging(TemplateInfo templateInfo, ArticleInfo articleInfo, Map<String, Object> data) throws BaseCheckedException {

        // 用于返回的list
        List<String> list = new ArrayList<String>();
        // 模板内容
        String templateContent = templateInfo.getContent();

        VMFactory vmFactory = new VMFactory(templateContent, data);
        data.put("parseringObjectId", "文章id=" + articleInfo.getId());
        String articleContent = articleInfo.getContent();
        // 判断内容使用了哪种分页符号并设值
        String splitword = whichPageBreak(articleContent);
        // 获取分页数
        int totalCount = 1;
        String[] contents = null;
        if (!StringUtil.isEmpty(splitword)) {// 分页
            contents = articleContent.split(splitword);
            totalCount = contents.length;
        }
        // 是否需要“单页显示全部”的页面
        if (articleInfo.isShowAllPage() && totalCount > 1) {
            data.put("content", articleContent.replaceAll(splitword, ""));
            data.put("split_line", "");
            list.add(vmFactory.getResultString());
        } else {
            list.add(null); // 设置list下标为0 的内容为空，表示不需要“单页显示全部”的页面
        }

        if (totalCount > 1) {// 分页
            data.put("PageCount", totalCount);
            // 构造分页的html内容
            PagerGenerator pg = new PagerGenerator(articleInfo.getId() + "", totalCount, articleInfo.isShowAllPage());
            data.put("title", articleInfo.getTitle());
            for (int i = 0; i < totalCount; i++) {
                StringBuffer sb = new StringBuffer();
                sb.append(pg.getPageHtml(i + 1));
                // TODO 最终文章将禁掉data语法
                data.put("data", this.getTemplateDataService(articleInfo.getChannelInfo()));
                data.put("curpage", i + 1);
                data.put("content", contents[i]);
                data.put("split_line", sb.toString());

                list.add(vmFactory.getResultString());
            }
        } else {// 不分页
            data.put("split_line", "");
            data.put("curpage", 1);
            data.put("PageCount", 1);
            data.put("content", articleContent);
            list.add(vmFactory.getResultString());
        }
        return list;
    }

    // 判断内容使用了哪种分页符号并设值
    private String whichPageBreak(String content) {
        if (StringUtil.isEmpty(content))
            return null;
        String splitword = null;
        String splitword1 = "<(hr|HR) color=(\")?#ff0123(\")? />";
        // String splitword2 =
        // "<div style=\"page-break-after:(\\s)?always(;)?\">((\\s)?&nbsp;)*<span style=\"(display|DISPLAY):(\\s)?none\">(\\s)*</span>(\\s)*</div>";
        String splitword2 = "<div style=\"page-break-after:(.)*?</div>";
        String splitword3 = "_baidu_page_break_tag_";
        //新版kindeditor在chorme下的分页符
        String splitword4 = "<hr style=\"page-break-after:always;\" class=\"ke-pagebreak\" />";
        if (content.split(splitword1).length > 1) {
            splitword = splitword1;
        } else if (content.split(splitword2).length > 1) {
            splitword = splitword2;
        } else if (content.split(splitword3).length > 1) {
            splitword = splitword3;
        } else if(content.indexOf(splitword4) != -1){
            splitword = splitword4;
        }
        return splitword;
    }

    /**
     * 获取某频道下面的 "配置文件" 模板
     * @param channelId
     * @return
     */
    private TemplateInfo getConfigTemplate(String channelId) {
        return templateRemoteService.getByChannelIdAndTemplateName(channelId, "配置文件");
    }

    /**
     * 获取公会新闻频道下面的 "资讯推广公用" 模板
     */
    private TemplateInfo getGHInfoPromotionTemplate() {
        return templateRemoteService.getByChannelIdAndTemplateName(GH_CHANNEL_ID, INFO_PROMOTION_TEMPLATE_NAME);
    }

    @Override
    public void parseTemplateAndGenerateFile(ChannelInfo channelInfo, TemplateInfo templateInfo) throws BaseCheckedException {
        List<HtmlPage> htmlPages = new ArrayList<HtmlPage>();
        try {
            htmlPages = this.parseTemplate(channelInfo, templateInfo);
            if (TemplateStatus.PASER_ERROR == templateInfo.getTemplateStatus()) {
                templateRemoteService.markParserSuccess(channelInfo.getId(), templateInfo.getId());
            }
        } catch (BaseCheckedException e) {
            templateRemoteService.markParserFail(channelInfo.getId(), templateInfo.getId(), e.getLocalizedMessage().substring(0, 200));
            logger.error("=================="+e.getLocalizedMessage()+"==================");
            e.printStackTrace();
            throw new BaseCheckedException(TemplateParserService.TEMPLATE_CAN_NOT_BE_PARSER, "解析模板[channelId=" + channelInfo.getId() + ", templateId=" + templateInfo.getId()
                    + "]失败");
        }
        for (HtmlPage htmlPage : htmlPages) {
            String path = null;
            if(templateInfo.getTemplateCategory() == TemplateCategory.PUBLIC){
                String channelId = channelInfo.getId();
                channelInfo = channelRemoteService.getById(PUBLIC_CHANNEL);
                String baseFilePath = channelInfo.getArticleFilePath();
                path = PathUtil.getAbsolutePublicTemplatePath(baseFilePath, channelId, templateInfo.getId(), htmlPage.getAlias(), htmlPage.getPageNo());
            }else{
               path = PathUtil.getAbsoluteTemplatePath(channelInfo.getArticleFilePath(), templateInfo.getId(), htmlPage.getAlias(), htmlPage.getPageNo());// 生成文件的目录
            }
            
            this.writeAndRsyncFileWithLog(channelInfo, path, htmlPage.getContent(), htmlPage.getCode(), true);
        }
    }

    @Override
    public void parseArticleAndGenerateFile(ChannelInfo channelInfo, ArticleInfo articleInfo) throws BaseCheckedException {
        List<HtmlPage> htmlPages = this.parseArticle(channelInfo, articleInfo);
        for (HtmlPage htmlPage : htmlPages) {
            String path = PathUtil.getAbsoluteArticlePath(channelInfo.getArticleFilePath(), articleInfo.getId(), htmlPage.getPageNo());// 生成文件的目录
            this.writeAndRsyncFileWithLog(channelInfo, path, htmlPage.getContent(), htmlPage.getCode(), true);
        }
    }

    @Override
    public List<HtmlPage> parseTag(String channelId, String tag) throws BaseCheckedException {
        // 返回的list
        List<HtmlPage> htmlPages = new ArrayList<HtmlPage>();

        if (StringUtil.hasOneEmpty(channelId, tag))
            return htmlPages;
        // 拿到channelInfo对象
        ChannelInfo channelInfo = channelRemoteService.getById(channelId);
        if (null == channelInfo)
            return htmlPages;
        // 拿到tagInfo对象
        TagInfo tagInfo = tagRemoteService.getByChannelIdAndName(channelId, tag);
        //if (null == tagInfo || tagInfo.getCategory() == TagCategory.SPECIAL) {
        if (null == tagInfo) {
            logger.info("' " + tag + " ' 是特殊tag，不解析");
            return htmlPages;
        }
        TemplateCategory category = tagInfo.getRelatedTemplateCategory(); // 如果有，只有可能是“标签模板
                                                                          // /
                                                                          // 标签图模板”两种之一
        if (null != category && category != TemplateCategory.TAG && category != TemplateCategory.TAG_VIEW)
            return htmlPages;
        // 拿到模板对象
        List<TemplateInfo> list = templateRemoteService.getByChannelIdAndCategoryOrderByCooperater(channelId, category);
        if (null == list || list.isEmpty())
            list = templateRemoteService.getByChannelIdAndCategoryOrderByCooperater(channelId, TemplateCategory.TAG);
        if (null == list || list.isEmpty())
            return htmlPages;

        // 填充数据
        Map<String, Object> data = new HashMap<String, Object>();
        data.put("guest", "");
        data.put("tools", new Tools());
        data.put("data", this.getTemplateDataService(channelInfo));
        data.put("channelid", channelId);
        // data.put("cooperate", "");
        data.put("channelname", channelInfo.getName());
        data.put("tag", tag);

        TemplateInfo templateInfo = list.get(0); // 默认获取第一个

        List<String> results = dealWithTagPaging(templateInfo, tagInfo, data);

        int i = 0;
        for (String result : results) {
            result = UserDefinedSyntaxParser.getInstance().parseTemplateQuote(channelInfo, templateInfo, result);
            htmlPages.add(new HtmlPage(result, ++i, templateInfo.getId(), templateInfo.getAlias(), templateInfo.getCode()));
        }

        return htmlPages;
    }

    @Override
    public void parseTagAndGenerateFile(ChannelInfo channelInfo, TagInfo tagInfo) throws BaseCheckedException {
        if (null == channelInfo || null == tagInfo)
            return;
        List<HtmlPage> htmlPages = this.parseTag(channelInfo.getId(), tagInfo.getName() + "");
        for (HtmlPage htmlPage : htmlPages) {
            String path = PathUtil.getAbsoluteTagPath(channelInfo.getArticleFilePath(), tagInfo.getId(), htmlPage.getPageNo());// 生成文件的目录
            this.writeAndRsyncFileWithLog(channelInfo, path, htmlPage.getContent(), htmlPage.getCode(), true);
        }
    }

    private List<String> dealWithTagPaging(TemplateInfo templateInfo, TagInfo tagInfo, Map<String, Object> data) throws BaseCheckedException {

        // 用于返回的list
        List<String> list = new ArrayList<String>();
        // 模板内容
        String content = templateInfo.getContent();
        // 自定义语法解析器
        UserDefinedSyntaxParser udsp = UserDefinedSyntaxParser.getInstance();
        // 分页解析，返回：1.content(被解析替换后的内容) 2.args(分页参数：总页数pages,每页显示数pagesize)
        List<String> contentAndArgs = udsp.parsePaging(content);
        boolean isNeedPage = (null != contentAndArgs && !contentAndArgs.isEmpty());
        if (isNeedPage) {
            content = contentAndArgs.get(0);
        }
        VMFactory vmFactory = new VMFactory(content, data);
        data.put("parseringObjectId", "标签id=" + tagInfo.getId());
        if (isNeedPage) {
            // 模板中的总页数
            int pages = Integer.parseInt(contentAndArgs.get(1));
            // 实际的总页数
            int totalPageCount = 0;
            // 每页显示数
            int pagesize = list.size() > 2 ? Integer.parseInt(contentAndArgs.get(2)) : udsp.getPageSizeFromContent(content);

            if (pagesize > 0) {
                // 获得总文章数
                String channelId = templateInfo.getChannelId();
                int articleCount = article4TagListRemoteService.getArticleCountByChannelIdAndTag(channelId, tagInfo.getName());
                articleCount = articleCount == 0 ? 1 : articleCount;// 没有文章的标签也要生成一页
                totalPageCount = (articleCount + pagesize - 1) / pagesize; // 计算得到总页数
            }

            totalPageCount = (totalPageCount > pages || totalPageCount == 0) ? pages : totalPageCount;

            PagerGenerator pg = new PagerGenerator(tagInfo.getId() + ".html", totalPageCount);

            for (int i = 0; i < totalPageCount; i++) {
                StringBuffer sb = new StringBuffer();
                sb.append(pg.getPageHtml(i + 1));

                data.put("len", pagesize);
                data.put("pagecut", i);
                data.put("split_line", sb.toString());
                list.add(vmFactory.getResultString());
            }
        } else {
            data.put("split_line", "");
            list.add(vmFactory.getResultString());
        }
        return list;
    }

    // @Override
    // public List<HtmlPage> parseKeyword(Map<String, Object> param) throws
    // BaseCheckedException {
    // // TODO YZQ
    // return null;
    // }

    @Override
    public void parseEmptyLinkAndGenerateFile(Article4TagListInfo a) throws BaseCheckedException {

        String path = PathUtil.getAbsoluteArticlePath(a.getChannelInfo().getArticleFilePath(), a.getId(), 1);
        String content = "<meta http-equiv=\"Refresh\" content=\"0;URL=" + a.getEmptyLink() + "\" />";
        this.writeFileWithLog(a.getChannelInfo(), path, content);
    }

    @Override
    public List<HtmlPage> parseVote(Map<String, Object> param) throws BaseCheckedException {
        // TODO YZQ
        return null;
    }

    @Override
    public List<HtmlPage> parseVoteResult(Map<String, Object> param) throws BaseCheckedException {
        // TODO YZQ
        return null;
    }

    /**
     * 设置模板数据，包括评论的数据
     * @param articleInfo
     * @return
     */
    private Map<String, Object> getTemplateDataFromArticleInfo(ChannelInfo channelInfo, ArticleInfo articleInfo) {
        Map<String, Object> data = new HashMap<String, Object>();
        data.put("search", new Search());
        data.put("tools", new Tools());
        // TODO 新发布器不允许最终文章模板调用data语法,对于在旧发布器发布的文章在新发布器上更新时,会出现问题,
        // 处理方案：1.建专题,栏目之类的模板,可使用data语法.2.重建最终页模板,引用1建成的模板
        // templateDataService.setChannelInfo(channelInfo);
        // data.put("data", templateDataService);
        data.put("channelid", channelInfo.getId());
        data.put("guest", "");
        data.put("channelid", articleInfo.getChannelInfo().getId());
        data.put("articleid", articleInfo.getId().toString());
        data.put("titlecolor", articleInfo.getTitleColor());
        data.put("channelname", articleInfo.getChannelInfo().getName());
        data.put("source", articleInfo.getSource());
        data.put("digest", articleInfo.getDigest());
        data.put("userid", articleInfo.getUserId());
        data.put("author", articleInfo.getAuthor());
        data.put("title", articleInfo.getTitle());
        data.put("subtitle", articleInfo.getSubtitle());
        if (articleInfo.getPublishTime() != null) {
            data.put("posttime", DateUtil.format(articleInfo.getPublishTime(), DateUtil.defaultDateTimePatternStr));
        }
        data.put("diy1", articleInfo.getDiy1());
        data.put("diy2", articleInfo.getDiy2());
        data.put("diy3", articleInfo.getDiy3());
        data.put("diy4", articleInfo.getDiy4());
        data.put("diy5", articleInfo.getDiy5());
        data.put("picurl", articleInfo.getPictureUrl());
        data.put("power", articleInfo.getPower());
        data.put("tags", articleInfo.getTags());
        TemplateDataService templateDataService = this.getTemplateDataService(articleInfo.getChannelInfo());
        data.put("data", templateDataService);
        data.put("firsttags", templateDataService.getFirstTagUrl(articleInfo.getRealtags()));
        data.put("realtags", templateDataService.getTagsUrl(articleInfo.getRealtags(), ","));
        // @TODO 使用通用评论(fix me 需要保存isNeedComment数据)
        if (null != articleInfo.getThreadId() && articleInfo.getThreadId() > 0) {
            data.put("comment", UserDefinedSyntaxParser.getInstance().getCommentInclude(articleInfo.getChannelInfo(), articleInfo.getId()));
        } else {
            data.put("comment", "");
        }
        return data;
    }

    // 热词替换
    private void replaceHotWord(ChannelInfo channelInfo, ArticleInfo articleInfo) {
        String content = articleInfo.getContent();
        // 在文章内容中将频道的热词替换成相应的链接
        List<HotWordInfo> hotWordList = hotWordRemoteService.getByChannelId(channelInfo.getId());
        if (null != hotWordList && !hotWordList.isEmpty()) {
            // 构造替换的map
            Map<String, String> map = new HashMap<String, String>();
            for (HotWordInfo h : hotWordList) {
                StringBuffer sb = new StringBuffer();
                sb.append("<a href=\"");
                sb.append(h.getHref());
                sb.append("\" target=\"_blank\" >");
                sb.append(h.getName());
                sb.append("</a>");
                map.put(h.getName(), sb.toString());
            }
            LexTree<String> lex = HotWordReplaceUtil.genLexTree(map);
            content = HotWordReplaceUtil.quickReplace(content, lex, new String[] { "<a.*?/a>", "<[^>]*>" });
            articleInfo.setContent(content);
        }

    }

    private void dealWithImagesInContent(ArticleInfo articleInfo) {
        if (null == articleInfo)
            return;
        // 随机处理图片域名
        ChannelInfo channelInfo = articleInfo.getChannelInfo();
        if (null == channelInfo)
            return;
        String imgDomain = channelInfo.getPicDomain().trim();
        if (imgDomain.indexOf("http://") != -1) {
            imgDomain = imgDomain.substring("http://".length());
        }
        int index = imgDomain.indexOf("/");
        if (index > 0) {
            imgDomain = imgDomain.substring(0, index);
        }
        // imgDomain.split("/")[0];
        String content = filtrateDuowanImages(imgDomain, articleInfo.getContent());
        content = addPicAlt(content, articleInfo.getTitle());
        articleInfo.setContent(content);
    }

    /**
     * 对文章中的图片链接进行处理,把pic.duowan.com替换成pic(1至5).duowan.com
     * @param content
     * @return
     */
    private String filtrateDuowanImages(String imgDomain, String content) {
        if (StringUtil.isEmpty(content))
            return content;
        String url = "";
        Pattern p = Pattern.compile("<img.*?src=\"?\\s?(http://" + imgDomain + "[^\\s>\"]+)\"?", Pattern.CASE_INSENSITIVE | Pattern.DOTALL);
        Matcher m = p.matcher(content);
        while (m.find()) {
            url = m.group(1);
            if (StringUtil.isEmpty(url))
                continue;
            content = content.replaceFirst(url, this.picUrlRandom(imgDomain, url));
        }
        return content;
    }

    /**
     * pic.duowan.com域名做随机处理
     * @param url
     * @return
     */
    // TODO pic.duowan.com --> img.duowan.com
    private String picUrlRandom(String imgDomain, String url) {
        if (StringUtil.isEmpty(url))
            return "";
        int index = imgDomain.indexOf(".");
        String pre = imgDomain.substring(0, index);
        String suf = imgDomain.substring(index);
        int x = (int) (Math.random() * 6);
        if (x == 0) {
            return url;
        } else {
            String replaceDomain = pre + x + suf;
            return url.replaceFirst(imgDomain, replaceDomain);
        }
    }

    /**
     * 为文章中alt为空的图片修改alt为标题的内容
     * @param content
     * @param title
     * @return
     */
    private String addPicAlt(String content, String title) {
        title = title.replaceAll("\"", "'");
        title = title.replaceAll("\\$", "");

        String regStr, repStr;
        regStr = "alt=\"\"";
        repStr = "alt=\"" + title + "\"";
        content = content.replaceAll(regStr, repStr);

        return content;
    }

    private TemplateDataService getTemplateDataService(ChannelInfo channelInfo) {
        TemplateDataService templateDataService = SpringApplicationContextHolder.getBean("templateDataService", TemplateDataService.class);
        templateDataService.setChannelInfo(channelInfo);
        return templateDataService;
    }

    /**
     * 写文件并记录到 log （用于同步的log）
     * @throws BaseCheckedException 
     */
    private void writeAndRsyncFileWithLog(ChannelInfo channelInfo, String filename, String content, String encode, boolean mkdir) throws BaseCheckedException {
        if (StringUtil.isEmpty(filename)) {
            logger.warn("文件路径为空");
            return;
        }
        // 生成文件
        try {
            FileUtil.write(filename, content, encode, mkdir);
            logger.info("成功生成文件:" + filename);
        } catch (IOException e) {
            logger.error("清空文件" + filename + "失败，原因：" + e.getMessage());
            throw new BaseCheckedException(TemplateParserService.FILE_CAN_NOT_BE_GENERATED, "清空文件" + filename + "失败");
        }

        // 使用Rsync同步文件
        String osname = System.getProperty("os.name");
        if (osname.indexOf("Linux") > -1) {

            String rsyncFileName = channelInfo.getId() + "/" + filename.split(channelInfo.getArticleFilePath() + "/")[1];

            try {
                String rsyncScript = RSYNC_SCRIPT_FILE_PATH;
                if (!StringUtil.isEmpty(rsyncScript)) {
                    Runtime.getRuntime().exec(rsyncScript + " " + rsyncFileName);
                    // //输入同步的结果日志
                    // Process process =
                    // Runtime.getRuntime().exec(rsyncScript+" "+rsyncFileName);
                    // BufferedReader br = new BufferedReader(new
                    // InputStreamReader(process.getInputStream()));
                    // StringBuffer outputResult=new StringBuffer();
                    // String output = null;
                    // while (null != (output = br.readLine())){
                    // outputResult.append(output);
                    // }
                    // logger.info("文件"+filename+"同步结果输出："+outputResult.toString());
                    logger.info("成功rsync分发同步文件:" + filename);
                }
            } catch (IOException e1) {
                logger.error("rsync同步文件" + filename + "失败，原因：" + e1.getMessage());
                e1.printStackTrace();
            }

            // 生成文件日志, 一分钟生成的页面，在同一日志文件内
            String logPathName = GENERATE_FILE_LOG_FILE_PATH + PathUtil.FILE_SEPARATOR + (DateUtil.ignoreSecond(new Date()).getTime() / 1000);
            String logContent = "";
            if (FileUtil.isFileExists(logPathName)) {
                logContent = FileUtil.readFile(logPathName);
            }
            try {
                logContent += rsyncFileName;
                FileUtil.write(logPathName, logContent);
            } catch (Exception e) {
                logger.error("写入日志" + logPathName + "失败，原因：" + e);
            }

        }

    }

    private void writeFileWithLog(ChannelInfo channelInfo, String filename, String content) throws BaseCheckedException {
        this.writeAndRsyncFileWithLog(channelInfo, filename, content, "UTF-8", true);
    }

    public static void main(String[] args) {
        String channelid = "an";
        String filename = "/data2/www/cms.duowan.com/an/1304/230055605257.html";
        String rsyncFileName = channelid + "/" + filename.split("/data2/www/cms.duowan.com/an/")[1];
        System.out.println(rsyncFileName);
    }

}
